﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Speech.Synthesis;
using System.Speech.Recognition;

namespace Moe_v0._1
{
    public partial class FormUI : Form
    {
        public FormUI()
        {
            InitializeComponent();

            // Must obtain reference of a text box which is going to output debug info
            // before MoeDebugger starts functioning.
            MoeDebugger.DebugBuffer = textBoxDebug;

            // Before release version, we set IsDebugging true by default.
            MoeDebugger.IsDebugging = true;

            // ...
            EnWordDictionary.InitializeDictionaryFromFile(@".\Model\高考英语3500词汇表.txt");

            // ...
            EnPrefixTreeModel.SetModelFileFullName(@".\Model\QA.txt");

            // This must be the last to be initialized.
            _moe = MoeAI.Instance();

#if false
            //////////////////////////////////////////////////////////////////////////
            // For TTS.
            synth = new SpeechSynthesizer();
            synth.SelectVoiceByHints(VoiceGender.Female);

            // Output information about all of the installed voices. 
            foreach (InstalledVoice voice in synth.GetInstalledVoices())
            {
                VoiceInfo info = voice.VoiceInfo;

                MoeDebugger.AddDebugInfo(" Name:          " + info.Name);
                MoeDebugger.AddDebugInfo(" Culture:       " + info.Culture);
                MoeDebugger.AddDebugInfo(" Age:           " + info.Age);
                MoeDebugger.AddDebugInfo(" Gender:        " + info.Gender);
                MoeDebugger.AddDebugInfo(" Description:   " + info.Description);
                MoeDebugger.AddDebugInfo(" ID:            " + info.Id);
            }
            // Configure the audio output. 
            synth.SetOutputToDefaultAudioDevice();

            //////////////////////////////////////////////////////////////////////////
            // For recognition.
            // Create a new SpeechRecognizer instance.
            sr = new SpeechRecognizer();

            // Create a simple grammar that recognizes all question patterns in QA.txt.
            Choices questions = new Choices();
            questions.Add(EnPrefixTreeModel.Instance().AllQuestions.ToArray());

            // Create a GrammarBuilder object and append the Choices object.
            GrammarBuilder gb = new GrammarBuilder();
            gb.Append(questions);

            // Create the Grammar instance and load it into the speech recognition engine.
            Grammar g = new Grammar(gb);
            sr.LoadGrammar(g);

            // Register a handler for the SpeechRecognized event.
            sr.SpeechRecognized +=
              new EventHandler<SpeechRecognizedEventArgs>(sre_SpeechRecognized);
#endif
        }

        // Create a simple handler for the SpeechRecognized event.
        void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            // MessageBox.Show("Speech recognized: " + e.Result.Text);
            _moe.RecieveQuestion(e.Result.Text);
            string answer = _moe.AnswerQuestion();
            textBoxOutput.AppendText("you: " + e.Result.Text + "\r\n");
            textBoxOutput.AppendText("moe: " + answer + "\r\n");

            synth.Speak(answer);
        }

        private SpeechSynthesizer synth;
        private SpeechRecognizer sr;
        private static MoeAI _moe = null;
        private void buttonSend_Click(object sender, EventArgs e)
        {
            if (textBoxInput.Text.Length > 0)
            {
                // User did input something.
                string userInput = textBoxInput.Text;
                textBoxOutput.AppendText("you: " + userInput + "\r\n");
                textBoxInput.Clear();

                #region VCZH
                // textBoxOutput.AppendText(string.Format("vczh: {0}\r\n", Vczh.FindAnswer(userInput)));
                #endregion

                // MoeAI process and answer/compile the question/instruction.
                // Instruction Filter judges whether the input is an instruction.
                EnInstructionFilter iFilter = new EnInstructionFilter();

                if (iFilter.IsInstruction("") == true)
                {
#if false
                    // If input is an instruction we try to understand it and compile.
                    _moe.RecieveInstruction(userInput);
                    _moe.ComplieInstruction();
#endif
                }
                else
                {
                    // Otherwise we try to answer the question from the user.
                    _moe.RecieveQuestion(userInput);
                    string answer = _moe.AnswerQuestion();
                    textBoxOutput.AppendText("moe: " + answer + "\r\n");

#if false
                    // Speak a text string synchronously.
                    synth.Speak(answer);
#endif
                }
            }
            else
            {
                MessageBox.Show("Please input something before you send it!");
            }
        }

        private void buttonDebug_Click(object sender, EventArgs e)
        {
            if (buttonDebug.Text == "Disable Debug")
            {
                MoeDebugger.IsDebugging = false;
                labelDebugState.Text = "Normal Running...";
                buttonDebug.Text = "Enable Debug";
            }
            else
            {
                MoeDebugger.IsDebugging = true;
                labelDebugState.Text = "Debugging...";
                buttonDebug.Text = "Disable Debug";
            }
        }

        private void buttonClearDebug_Click(object sender, EventArgs e)
        {
            MoeDebugger.DebugBuffer.Clear();
        }

        private bool _enterPressed = false;
        private void textBoxInput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                buttonSend_Click(sender, e);
                _enterPressed = true;
            }
        }

        private void textBoxInput_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (_enterPressed)
            {
                // http://msdn.microsoft.com/zh-cn/library/system.windows.forms.control.keydown(v=vs.80).aspx
                e.Handled = true;
                _enterPressed = false;
            }
        }
    }

    static class Vczh
    {
        static Dictionary<string, string[]> answers = new Dictionary<string, string[]>();

        static Vczh()
        {
            foreach (var line in File.ReadAllLines(@".\Model\QA.txt"))
            {
                var fragments = line
                    .Split('|');
                answers.Add(fragments[0].Trim().ToUpper(), fragments.Skip(1).ToArray());
            }
        }

        public static string FindAnswer(string input)
        {
            input = " " + input.ToUpper() + " ";

            var bounds = Enumerable.Range(0, input.Length - 1)
                .Where(i => ('A' <= input[i] && input[i] <= 'Z') ^ ('A' <= input[i + 1] && input[i + 1] <= 'Z'))
                .ToArray();

            var fragments =
                bounds.Where((i, j) => j % 2 == 0)
                .Zip(
                    bounds.Where((i, j) => j % 2 == 1),
                    Tuple.Create
                    )
                .Select(t => input.Substring(t.Item1 + 1, t.Item2 - t.Item1))
                .ToArray();

            var prefix = fragments.Aggregate("", (a, b) => a + " " + b).Trim();
            string[] candidates = null;
            if (answers.TryGetValue(prefix, out candidates))
            {
                // always return the first answer
                return candidates[0];
            }
            else
            {
                return "Sorry, I don't understand!";
            }
        }
    }
}
